<?php
declare (strict_types=1);

namespace sws;

class Config
{

    /**
     * 配置实例
     * @var Config
     */
    protected static $instance;

    /**
     * 配置参数
     * @var array
     */
    protected static $config = [];

    /**
     * 配置文件路径
     * @var string
     */
    protected $path = '';

    /**
     * 配置文件后缀
     * @var string
     */
    protected $ext = '.php';


    /**
     * 获取实例
     * @return Config
     */
    public static function getInstance(): Config
    {
        return Container::getInstance()->make(Config::class);
    }


    /**
     * Config constructor.
     * @param string $path 配置文件目录
     * @param string $ext 配置文件后缀名
     */
    public function __construct(string $path = '', string $ext = '')
    {
        if (!$path) {
            $path = BASE_PATH . 'config';
        }
        if (!$ext) {
            $ext = '.php';
        }
        $this->path = $path;
        $this->ext = $ext;
    }

    /**
     * 初始化配置
     * @param false $force 是否强制重新加载配置（强制重新获取会从配置目录重新获取配置）
     * @return $this
     */
    public function initialize(bool $force = false): Config
    {
        if (false === $force) {
            if (!empty(self::$config)) {
                return $this;
            }
        }
        return $this->loadPath();
    }

    /**
     * 加载路径配置文件
     * @param string $path 加载路径
     * @param string $name 配置项键名
     * @return $this
     */
    private function loadPath(string $path = '', string $name = ''): Config
    {
        if (!$path) {
            $path = $this->path;
        }
        $files = scandir($path);
        foreach ($files as $file) {
            if ($file == '.' || $file == '..') {
                continue;
            }
            //如果是目录则递归目录配置
            if (is_dir($path . '/' . $file)) {
                $this->loadPath($path . '/' . $file, $file);
                continue;
            }

            if (is_file($path . '/' . $file) && substr($file, -strlen($this->ext)) === $this->ext) {
                $key = substr($file, 0, strlen($file) - strlen($this->ext));
                if ($name) {
                    /** @noinspection PhpIncludeInspection */
                    self::$config[$name][$key] = require $path . '/' . $file;
                } else {
                    /** @noinspection PhpIncludeInspection */
                    self::$config[$key] = require $path . '/' . $file;
                }
            }
        }
        return $this;
    }

    /**
     * 获取配置参数
     * @param string $name 配置参数名（支持多级获取 . 号分割）
     * @param null $default 获取失败默认值
     * @return array|mixed|null
     */
    public function get(string $name = '', $default = null)
    {
        if (empty($name)) {
            return self::$config;
        }
        if (false === strpos($name, '.')) {
            return self::$config[$name] ?? $default;
        }

        $name = explode('.', $name);
        $config = self::$config;

        foreach ($name as $value) {
            if (isset($config[$value])) {
                $config = $config[$value];
            } else {
                return $default;
            }
        }
        return $config;
    }

    /**
     * 设置配置参数
     * @param string|array $name 要设置的键名（支持多级获取 . 号分割和数组批量设置）
     * @param null $value 要设置的值
     * @return bool
     */
    public function set($name, $value = null): bool
    {
        if (is_array($name)) {
            self::$config = array_merge(self::$config, $name);
            return true;
        } elseif (is_string($name)) {
            $nameArray = array_reverse(explode('.', $name));
            $config = [];
            foreach ($nameArray as $key => $item) {
                if ($key === 0) {
                    $config[$item] = $value;
                } else {
                    $config[$item] = $config;
                    unset($config[$nameArray[$key - 1]]);
                }
            }
            self::$config = $this->arrayMergeDeep(self::$config, $config);
            return true;
        }
        return false;
    }

    /**
     * 递归合并多维数组
     * @param ...$arrays
     * @return array
     */
    protected function arrayMergeDeep(...$arrays): array
    {
        $merged = [];
        while ($arrays) {
            $array = array_shift($arrays);
            if (!$array) {
                continue;
            }
            foreach ($array as $key => $value) {
                if (is_string($key)) {
                    if (is_array($value) && array_key_exists($key, $merged)
                        && is_array($merged[$key])) {
                        $merged[$key] = $this->arrayMergeDeep(...[$merged[$key], $value]);
                    } else {
                        $merged[$key] = $value;
                    }
                } else {
                    $merged[] = $value;
                }
            }
        }
        return $merged;
    }

    /**
     * 检测配置值是否存在
     * @param string $name
     * @return bool
     */
    public function has(string $name): bool
    {
        if (false === strpos($name, '.') && !isset(self::$config[$name])) {
            return false;
        }
        return !is_null($this->get($name));
    }

}